﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Be.Windows.Forms;
using System.Threading;
using System.IO;

namespace robotGUI
{
    public partial class frmEepromEditor : Form
    {
        List<byte> content;
        DynamicByteProvider byteProvider;
        bool modified;

        public frmEepromEditor()
        {
            InitializeComponent();
        }

        frmMain.NewMessageEventHandler newMessage; //Receive robotMsg to listen for EEPROM_DIRECT
        robotSettings.ParameterArrivedEventHandler newParameter; //Receive fresh parameter EEPROM_SIZE
        private void frmEepromEditor_Load(object sender, EventArgs e)
        {
            newMessage = new frmMain.NewMessageEventHandler(frmEepromEditor_NewMsg);
            newParameter = new robotSettings.ParameterArrivedEventHandler(Settings_ParameterArrived);
            if (Owner != null)
            {
                //Register parameter listener
                ((frmMain)Owner).Settings.ParameterArrived += newParameter;
                getEepromSize();
            }
        }

        //Get fresh EEPROM_SIZE 
        private void getEepromSize()
        {
            status("Request parameter: EEPROM_SIZE", 0);
            progressBar1.Style = ProgressBarStyle.Marquee;
            ((frmMain)Owner).Settings.getValue(Command.EEPROM_SIZE);
            isBusy(true);
            timeoutTimer.Start();
            lastAddress = -1;
        }

        //response from getValue - find EEPROM_SIZE
        private void Settings_ParameterArrived(object sender, robotSettings.ParameterEventArgs e)
        {
            if (e.command == Command.EEPROM_SIZE)
            {
                //got EEPROM_SIZE
                timeoutTimer.Stop();
                //Create hexbox content
                content = new List<byte>();
                //for (int count = 0; count < dataSize; count++) content.Add(0x0); //don't bother
                byteProvider = new DynamicByteProvider(content);
                hexBox.ByteProvider = byteProvider;
                byteProvider.Changed += new EventHandler(byteProvider_Changed); //for 'Modified' features
                progressBar1.Style = ProgressBarStyle.Continuous;
                progressBar1.Maximum = ((frmMain)Owner).Settings.EEPROM_SIZE;
                hexBox.CursorChanged += new EventHandler(hexBox_CursorChanged);
                hexBox.SelectionStartChanged += new EventHandler(hexBox_CursorChanged);
                //de-register parameter listener
                ((frmMain)Owner).Settings.ParameterArrived -= newParameter;
                //Register message listener
                ((frmMain)Owner).NewMessage += newMessage;
                //start getting bytes
                readAll();
            }
        }

        void hexBox_CursorChanged(object sender, EventArgs e)
        {
            int sel = (int)hexBox.SelectionStart;
            byte data = 0;
            if (byteProvider.Bytes.Count < sel) data = byteProvider.Bytes[sel];
            lblPosition.Text = "@ 0x"
                + sel.ToString("X2")
                + ","
                + sel.ToString()
                + " = 0x"
                + data.ToString("X2")
                + ","
                + data.ToString();
        }

        //In-dialog status messages without progress
        void status(string msg)
        {
            progressBar1.Visible = false;
            lblStatus.Visible = true;
            lblStatus.Text = msg;
        }

        //In-dialog status with progress bar
        private void status(string msg, int prg)
        {
            progressBar1.Visible = true;
            progressBar1.Value = prg;
            lblStatus.Visible = true;
            if (msg != null) lblStatus.Text = msg;
        }

        //Alter UI controls and modified flag 
        private void isModified(bool isTrue)
        {
            modified = isTrue;
            this.Text = "EEPROM Editor";
            if (isTrue) this.Text += " (Modified)";
        }

        //Alter UI controls if busy
        private void isBusy(bool isTrue)
        {
            btnOpen.Enabled = btnErase.Enabled = btnRefresh.Enabled = btnSave.Enabled = btnWrite.Enabled = btnVerify.Enabled = btnRestore.Enabled = !isTrue;
            if (!isTrue) btnSave.Enabled = btnWrite.Enabled = btnVerify.Enabled = (((frmMain)Owner).Settings.EEPROM_SIZE != 0);
            if (isTrue)
            {
                panel2.Cursor = Cursors.WaitCursor;
                lblPosition.Text = "";
            }
            else panel2.Cursor = Cursors.Default;
        }

        bool verifying = false;
        bool verifyError;
        List<frmVerifyFail.VerifyFailEntry> verifyFailures;
        bool communicationError;
        int lastAddress;
        void frmEepromEditor_NewMsg(object sender, NewMessageEventArgs e)
        {
            communicationError = false;
            if (e.msg.msgType == robotMsg.Type.PAR && e.msg.parCommand == (byte)Command.EEPROM_DIRECT)
            {
                status(null, lastAddress = e.msg.parAddress);
                //Verifying
                if (verifying && byteProvider.ReadByte(e.msg.parAddress) != (byte)e.msg.parData)
                {        
                    verifyError = true;
                    verifyFailures.Add(new frmVerifyFail.VerifyFailEntry(e.msg.parAddress, byteProvider.ReadByte(e.msg.parAddress), (int)e.msg.parData));
                }
                //Reading
                else
                {
                    if (byteProvider.Length == e.msg.parAddress)
                        byteProvider.Bytes.Add((byte)e.msg.parData);
                    else if (byteProvider.Length < e.msg.parAddress)
                    {//Out of order? Lost a packet - fill with zero
                        communicationError = true;
                        while (byteProvider.Length < e.msg.parAddress)
                            byteProvider.Bytes.Add(0);
                        byteProvider.Bytes.Add((byte)e.msg.parData);
                    }
                    else
                        byteProvider.WriteByte(e.msg.parAddress, (byte)e.msg.parData);
                    byteProvider.ApplyChanges();
                    hexBox.Invalidate();
                }
                //Next EEPROM address request read
                if (e.msg.parAddress + 1 < ((frmMain)Owner).Settings.EEPROM_SIZE)
                {
                    ((frmMain)Owner).serialControl.readEe(e.msg.parAddress + 1);
                    //Reset one-shot timeout timer
                    timeoutTimer.Stop();
                    timeoutTimer.Start();
                }
                //Last byte - finish up
                else
                {
                    hexBox.Enabled = true;
                    //btnVerify.Enabled = true;
                    timeoutTimer.Stop();
                    //was verifying..
                    if (verifying)
                    {
                        if (verifyError)
                        {
                            status("Verify failed!");
                            frmVerifyFail failures = new frmVerifyFail();
                            failures.Fill(verifyFailures);
                            failures.Show();
                        }
                        else status("Verify Good.");
                        verifying = false;
                    }
                    //was reading..
                    else
                    {
                        status("Read Complete.");
                        isModified(false);
                    }
                    if (communicationError) status("Communication Error! Data invalid.");
                    isBusy(false);
                }
            }
        }

        //Hex editor modified
        void byteProvider_Changed(object sender, EventArgs e)
        {
            isModified(true);
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        //Opportunity to cancel close
        private void frmEepromEditor_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (modified && TopMostMessageBox.Show(this, "Exiting now will lose all changes.\nAre you sure you want to continue?", "Data Modified") == DialogResult.No)
            {
                e.Cancel = true;
                return;
            }
            //Deregister message receivers
            if (Owner != null)
            {
                ((frmMain)Owner).NewMessage -= newMessage;
                ((frmMain)Owner).Settings.ParameterArrived -= newParameter;
            }
        }

        //user refresh
        private void btnRefresh_Click(object sender, EventArgs e)
        {
            readAll();
        }

        //Start a read sequence
        private void readAll()
        {
            if (((frmMain)Owner).Settings.EEPROM_SIZE == 0) getEepromSize();
            else
            {
                if (verifying) status("Verifying..", 0);
                else //Read gets a chance to cancel if modified
                {
                    if (modified && TopMostMessageBox.Show(this, "Refreshing EEPROM will overwrite data.\nAll changes to data will be lost!\nAre you sure you want to continue?", "Data Modified") == DialogResult.No)
                        return;
                    status("Reading..", 0);
                }
                //Go..
                hexBox.Enabled = false;
                if (Owner != null) ((frmMain)Owner).serialControl.readEe(lastAddress = 0);
                timeoutTimer.Start();
                isBusy(true);
            }
        }

        //Timed out waiting for a EEPROM_DIRECT response
        private void timeoutTimer_Tick(object sender, EventArgs e)
        {
            if (lastAddress != -1)
            {
                status("Retrying..", lastAddress);
                if (Owner != null) ((frmMain)Owner).serialControl.readEe(lastAddress);
            }
            else
            {
                status("No Response!");
                timeoutTimer.Stop();
                verifying = false;
                isBusy(false);
            }
        }

        bool erasing = false;
        int writeAddress;
        //User write out data
        private void btnWrite_Click(object sender, EventArgs e)
        {
            writeAll();
        }

        //Start a timed write sequence
        private void writeAll()
        {
            if (erasing) status("Erasing..", 0);
            else status("Writing..", 0);
            hexBox.Enabled = false;
            writeAddress = 0;
            writeTimer.Enabled = true;
            isBusy(true);
        }

        //Time to write next byte..
        private void writeTimer_Tick(object sender, EventArgs e)
        {
            //Erasing? change data to 0xff(blank) 
            if (erasing)
            {
                byteProvider.Bytes[writeAddress] = 0xff;
                byteProvider.ApplyChanges();
                hexBox.Invalidate();
            }
            //Go..
            if (Owner != null) ((frmMain)Owner).serialControl.writeEe(writeAddress, byteProvider.Bytes[writeAddress]);
            status(null, writeAddress);
            //Write complete? finish up
            if (++writeAddress >= ((frmMain)Owner).Settings.EEPROM_SIZE)
            {
                writeTimer.Enabled = false;
                hexBox.Enabled = true;
                isBusy(false);
                if (erasing) status("Erase Complete.");
                else status("Write Complete.");
                erasing = false;
                isModified(false);
            }
        }

        //User verify
        private void btnVerify_Click(object sender, EventArgs e)
        {
            verifyError = false;
            verifying = true;
            verifyFailures = new List<frmVerifyFail.VerifyFailEntry>();
            readAll();
        }

        //User erase
        private void btnErase_Click(object sender, EventArgs e)
        {
            //Warning..
            if (TopMostMessageBox.Show(this, "Erasing EEPROM will revert all settings to factory default.\nAll configuration and tuning parameters will be lost!\nAre you sure you want to continue?", "Erase EEPROM") == DialogResult.Yes)
            {
                //Do it!
                if (byteProvider != null)
                {
                    erasing = true;
                    writeAll();
                }
            }
        }

        //Load hex editor from Intel HEX file
        private void btnOpen_Click(object sender, EventArgs e)
        {
            if (Properties.Settings.Default.hexDirectory == null || Properties.Settings.Default.hexDirectory == "")
                Properties.Settings.Default.hexDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            openFileDialog1.InitialDirectory = Properties.Settings.Default.hexDirectory;
            if (openFileDialog1.ShowDialog(this) == DialogResult.OK)
            {
                Properties.Settings.Default.hexDirectory = Path.GetDirectoryName(openFileDialog1.FileName);
                IntelHex ihex = new IntelHex();
                StreamReader reader = new StreamReader(openFileDialog1.FileName);
                IntelHexStructure hex = ihex.Read(reader);
                if (hex.dataLen != byteProvider.Length && TopMostMessageBox.Show(this, "Hex file size does not suit parameters.\nSize is " + hex.dataLen.ToString() + ", should be " + ((frmMain)Owner).Settings.EEPROM_SIZE.ToString() + " bytes.\nAre you sure you want to use this file?", "Hex file size incorrect") == DialogResult.No)
                {
                    status("HEX Load Cancelled.");
                    return;
                }
                for (int a = 0; a < byteProvider.Length; a++) byteProvider.WriteByte(a, 0); //clear data
                for (int a = 0; a < byteProvider.Length && a < hex.dataLen; a++)
                    byteProvider.WriteByte(a, hex.data[a]);
                isModified(true);
                hexBox.Enabled = true;
                hexBox.Invalidate();
                status("HEX File Loaded.");
            }
        }

        //Save hex editor to Intel HEX file
        private void btnSave_Click(object sender, EventArgs e)
        {
            if (Properties.Settings.Default.hexDirectory == null || Properties.Settings.Default.hexDirectory == "")
                Properties.Settings.Default.hexDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            saveFileDialog1.InitialDirectory = Properties.Settings.Default.hexDirectory;
            saveFileDialog1.FileName = Properties.Settings.Default.hexFile;
            if (saveFileDialog1.ShowDialog(this) == DialogResult.OK)
            {
                Properties.Settings.Default.hexFile = Path.GetFileName(saveFileDialog1.FileName);
                Properties.Settings.Default.hexDirectory = Path.GetDirectoryName(saveFileDialog1.FileName);
                IntelHex ihex = new IntelHex();
                ihex.NewRecord(HexType.IHEX_TYPE_DATA, 0, content.ToArray(), content.Count);
                StreamWriter writer = new StreamWriter(saveFileDialog1.FileName);
                ihex.Write(writer);
                writer.Close();
                status("HEX File Saved.");
            }
        }

        //Send an EEPROM_RESTORE command - forces remote to re-write EEPROM contents
        private void btnRestore_Click(object sender, EventArgs e)
        {
            if (modified && TopMostMessageBox.Show(this, "Restoring EEPROM will overwrite data.\nAll changes to data will be lost!\nAre you sure you want to continue?", "Data Modified") == DialogResult.No)
                return;
            //This send is unbuffered 
            ((frmMain)Owner).serialControl.sendValue(Command.EEPROM_RESTORE, 0);
            modified = false;
            readAll();
        }

        //public static System.Windows.Forms.DialogResult WW_MessageBox(string Message, string Caption,
        //    System.Windows.Forms.MessageBoxButtons buttons, System.Windows.Forms.MessageBoxIcon icon,
        //    System.Windows.Forms.MessageBoxDefaultButton defaultButton)
        //{
        //    return System.Windows.Forms.MessageBox.Show(Message, Caption, buttons, icon, defaultButton,
        //        (System.Windows.Forms.MessageBoxOptions)8192 /*MB_TASKMODAL*/);
        //
        //}
    }
    static public class TopMostMessageBox
    {
        static public DialogResult Show(IWin32Window owner, string message, string title)
        {
            frmDialogBox dialog = new frmDialogBox(title, message);
            DialogResult result = dialog.ShowDialog(owner);
            dialog.Dispose(); // clean it up all the way
            return result;
        }
    }
}


